package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/csingo/cHelper"
	"gitee.com/csingo/cLog"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"satis-go/base/collector"
	"satis-go/config/vars"
	"strings"
)

type GitService struct {
	collector.GitServiceHTTPServer
}

type BranchHash struct {
	Hash string
	Name string
}

func (i *GitService) Download(ctx *gin.Context, req *collector.GitServiceDownloadReq) (rsp *collector.GitServiceDownloadRsp, err error) {
	defer func() {
		if r := recover(); r != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"req": req,
				"r":   r,
			}).Error("GitService.Download:捕获异常")
			err = errors.New(fmt.Sprintf("%s:%s", "GitService.Download:捕获异常", r))
		}
	}()

	rsp = &collector.GitServiceDownloadRsp{
		Versions:        []string{},
		IsUpdateVersion: false,
	}
	// 检出代码
	clonePath := filepath.Clean(fmt.Sprintf("%s/git/%s", vars.CollectorConfig.PackagesPath, strings.ReplaceAll(req.Name, "/", "-")))
	os.RemoveAll(clonePath)
	cloneOption := cHelper.CmdOption{
		Command: fmt.Sprintf("git clone %s %s", req.Url, clonePath),
	}
	defer func() {
		// 清理文件
		err = os.RemoveAll(clonePath)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"checkoutPath": clonePath,
			}).Error("GitService.Download:清空代码目录失败")
		}
	}()
	cloneRes, err := cHelper.Cmd(cloneOption)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"checkoutOption": cloneOption,
			"res":            string(cloneRes),
			"err":            err.Error(),
		}).Error("GitService.Download:clone代码异常")
		return
	} else {
		cLog.WithContext(ctx, map[string]interface{}{
			"res":            string(cloneRes),
			"checkoutOption": cloneOption,
		}).Debug("GitService.Download:clone代码成功")
	}

	// 获取分支
	headCmdOption := cHelper.CmdOption{
		Command: "git show-ref --heads",
		Dir:     clonePath,
	}
	headsRes, err := cHelper.Cmd(headCmdOption)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"headCmdOption": headCmdOption,
			"checkoutPath":  clonePath,
			"err":           err.Error(),
		}).Error("GitService.Download:获取分支失败")
		return
	}
	heads := strings.Split(string(headsRes), "\n")
	hashes := []*BranchHash{}
	for _, head := range heads {
		if head == "" {
			continue
		}
		headHashes := strings.Split(head, " ")
		hashes = append(hashes, &BranchHash{
			Hash: headHashes[0],
			Name: fmt.Sprintf("dev-%s", strings.TrimPrefix(headHashes[1], "refs/heads/")),
		})
	}
	// 获取tag
	tagCmdOption := cHelper.CmdOption{
		Command: "git show-ref --tags",
		Dir:     clonePath,
	}
	tagsRes, err := cHelper.Cmd(tagCmdOption)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"tagCmdOption": tagCmdOption,
			"checkoutPath": clonePath,
			"err":          err.Error(),
		}).Error("GitService.Download:获取tag失败")
		return
	}
	tags := strings.Split(string(tagsRes), "\n")
	for _, tag := range tags {
		if tag == "" {
			continue
		}
		tagHashes := strings.Split(tag, " ")
		tagName := strings.TrimPrefix(tagHashes[1], "refs/tags/")
		if !regexp.MustCompile(`^(v*)([0-9]+\.?)+$`).MatchString(tagName) {
			continue
		}
		hashes = append(hashes, &BranchHash{
			Hash: tagHashes[0],
			Name: tagName,
		})
	}

	//cLog.WithContext(ctx, map[string]interface{}{
	//	"hashes": hashes,
	//}).Debug("GitService.Download:获取分支信息")

	// 初始化include和p2
	includePackages := &collector.IncludePackages{
		Packages: map[string]map[string]interface{}{
			req.Name: {},
		},
	}
	p2Packages := &collector.P2Packages{
		Packages: map[string][]interface{}{
			req.Name: {},
		},
	}

	// 读取已有的include
	includePath := filepath.Clean(fmt.Sprintf("%s/include/%s.json", vars.CollectorConfig.PackagesPath, req.Name))
	includeDir := filepath.Dir(includePath)
	if !cHelper.IsExistsPath(includeDir) {
		err = os.MkdirAll(includeDir, 0755)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"req": req,
				"dir": includeDir,
				"err": err.Error(),
			}).Error("PackagistService.Download:创建include目录失败")
			return
		}
	}
	includeJsonBody, err := ioutil.ReadFile(includePath)
	if err == nil {
		json.Unmarshal(includeJsonBody, includePackages)
	}

	// 读取已有的p2
	p2Path := filepath.Clean(fmt.Sprintf("%s/p2/%s.json", vars.CollectorConfig.PackagesPath, req.Name))
	p2Dir := filepath.Dir(p2Path)
	if !cHelper.IsExistsPath(p2Dir) {
		err = os.MkdirAll(p2Dir, 0755)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"req": req,
				"dir": p2Dir,
				"err": err.Error(),
			}).Error("PackagistService.Download:创建p2目录失败")
			return
		}
	}
	p2JsonBody, err := ioutil.ReadFile(p2Path)
	if err == nil {
		json.Unmarshal(p2JsonBody, p2Packages)
	}

	result := []string{}
	for _, hash := range hashes {
		result = append(result, hash.Name)
		// 压缩包名
		zipName := fmt.Sprintf("%s-%s.zip", strings.ReplaceAll(req.Name, "/", "-"), hash.Hash)
		zipPath := fmt.Sprintf("%s/dist/%s/%s", vars.CollectorConfig.PackagesPath, req.Name, zipName)
		if cHelper.IsExistsPath(zipPath) {
			continue
		}
		// checkout代码
		checkoutOption := cHelper.CmdOption{
			Command: fmt.Sprintf("git checkout %s", hash.Hash),
			Dir:     clonePath,
		}
		_, err = cHelper.Cmd(checkoutOption)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"checkoutOption": checkoutOption,
				"err":            err.Error(),
			}).Error("GitService.Download:checkout代码失败")
			return
		}
		cLog.WithContext(ctx, map[string]interface{}{
			"hash": hash,
		}).Debug("GitService.Download:checkout代码成功")
		// 判断是否composer包
		composerJsonPath := fmt.Sprintf("%s/composer.json", clonePath)
		if !cHelper.IsExistsPath(composerJsonPath) {
			continue
		}
		// 获取composer.json
		composerJsonContentBytes, err := ioutil.ReadFile(composerJsonPath)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"composerJsonPath": composerJsonPath,
				"err":              err.Error(),
			}).Error("GitService.Download:获取composer.json失败")
			continue
		}
		composerJsonData := map[string]interface{}{}
		err = json.Unmarshal(composerJsonContentBytes, &composerJsonData)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"composerJsonPath": composerJsonPath,
				"err":              err.Error(),
			}).Error("GitService.Download:解析composer.json失败")
			continue
		}
		composerJsonData["version"] = hash.Name
		composerJsonData["source"] = map[string]string{
			"reference": hash.Hash,
			"type":      "git",
			"url":       req.Url,
		}
		composerJsonData["dist"] = map[string]string{
			"reference": hash.Hash,
			"shasum":    "",
			"type":      "zip",
			"url":       fmt.Sprintf("%s/dist/%s/%s", vars.CollectorConfig.SatisHost, req.Name, zipName),
		}
		delete(composerJsonData, "minimum-stability")
		// 加入include和p2
		includePackages.Packages[req.Name][hash.Name] = composerJsonData
		p2Packages.Packages[req.Name] = append(p2Packages.Packages[req.Name], composerJsonData)
		//cLog.WithContext(ctx, map[string]interface{}{
		//	"composerJsonData": composerJsonData,
		//}).Debug("GitService.Download:获取composer.json成功")
		// 生成压缩包
		err = cHelper.Zip(clonePath, zipPath, []string{".git", ".idea", "vendor"}...)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"clonePath": clonePath,
				"zipPath":   zipPath,
				"err":       err.Error(),
			}).Error("GitService.Download:压缩代码失败")
			return rsp, err
		}
		rsp.IsUpdateVersion = true
	}

	// 生成include
	includePackagesJsonBody, err := json.Marshal(includePackages)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"includePackages": includePackages,
			"err":             err.Error(),
		}).Error("GitService.Download:include json格式化失败")
		return
	}
	includePackagesJsonContent := string(includePackagesJsonBody)
	// 生成p2
	p2PackagesJsonBody, err := json.Marshal(p2Packages)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"req": req,
			"err": err.Error(),
		}).Error("GitService.Download:p2 json格式化失败")
		return
	}
	p2PackagesJsonContent := string(p2PackagesJsonBody)

	// 写include文件
	err = ioutil.WriteFile(includePath, []byte(includePackagesJsonContent), 0755)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"req":  req,
			"path": includePath,
			"err":  err.Error(),
		}).Error("PackagistService.Download:写include文件失败")
		return
	} else {
		cLog.WithContext(ctx, map[string]interface{}{
			"req":  req,
			"path": includePath,
		}).Debug("PackagistService.Download:写include文件成功")
	}

	// 写p2文件
	err = ioutil.WriteFile(p2Path, []byte(p2PackagesJsonContent), 0755)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"req":  req,
			"path": p2Path,
			"err":  err.Error(),
		}).Error("PackagistService.Download:写p2文件失败")
		return
	} else {
		cLog.WithContext(ctx, map[string]interface{}{
			"req":  req,
			"path": p2Path,
		}).Debug("PackagistService.Download:写p2文件成功")
	}

	rsp.Versions = result

	return
}
